package com.yalu.dao.base.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.yalu.bean.Pager;
import com.yalu.bean.Pager.OrderType;
import com.yalu.dao.base.BaseDao;

/**
 * Dao实现类 - Dao实现类基类
 */

@Repository
public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

	private Class<T> entityClass;
	protected SessionFactory sessionFactory;
	protected DriverManagerDataSource dataSource;
	protected JdbcTemplate jdbcTemplate;
	

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.entityClass = null;
        Class c = getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClass = (Class<T>) parameterizedType[0];
        }
	}
	
	@Resource
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public T load(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public List<T> get(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		String hql = "from " + entityClass.getName() + " as model where model.id in(:ids)";
		return getSession().createQuery(hql).setParameterList("ids", ids).list();
	}

	@SuppressWarnings("unchecked")
	public T get(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return (T) getSession().createQuery(hql).setParameter(0, value).uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public T get(String[] propertyNames, Object[] values) {
		StringBuffer hql = new StringBuffer("from " + entityClass.getName() + " as model ");
		for(int i=0;i<propertyNames.length;i++){    //关联查询
			if(propertyNames[i].indexOf(".")>-1){
				String[] arr = StringUtils.split(propertyNames[i], ".");
				hql.append(" left join model."+arr[0]+" as o ");
			}
		}
		hql.append(" where 1=1 ");
		for(int i=0;i<propertyNames.length;i++){    //关联查询
			if(propertyNames[i].indexOf(".")>-1){
				String[] arr = StringUtils.split(propertyNames[i], ".");
				hql.append(" and o."+arr[1]+"=?");
			} else {
				hql.append(" and model."+propertyNames[i]+"=? ");
			}
		}
		//System.out.println(hql);
		Query query =  getSession().createQuery(hql.toString());
		for(int i=0;i<values.length;i++){
			query.setParameter(i, values[i]);
		}
		return (T)query.uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getList(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return getSession().createQuery(hql).setParameter(0, value).list();
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		String hql = "from " + entityClass.getName();
		return getSession().createQuery(hql).list();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAll(String sortProperty) {
		String sortOrder = "asc";
		return getAll( sortProperty,sortOrder);
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAll(String sortProperty,String sortOrder) {
		String hql = "from " + entityClass.getName() + " order by "+sortProperty+" "+sortOrder;
		return getSession().createQuery(hql).list();
	}
	
	
	
	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	public boolean isUnique(String propertyName, Object oldValue, Object newValue) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(newValue, "newValue is required");
		if (newValue == oldValue || newValue.equals(oldValue)) {
			return true;
		}
		if (newValue instanceof String) {
			if (oldValue != null && StringUtils.equalsIgnoreCase((String) oldValue, (String) newValue)) {
				return true;
			}
		}
		T object = get(propertyName, newValue);
		return (object == null);
	}
	
	public boolean isExist(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		T object = get(propertyName, value);
		return (object != null);
	}

	@SuppressWarnings("unchecked")
	public PK save(T entity) {
		Assert.notNull(entity, "entity is required");
		return (PK) getSession().save(entity);
	}

	public void update(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().update(entity);
	}

	public void delete(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().delete(entity);
	}

	public void delete(PK id) {
		Assert.notNull(id, "id is required");
		T entity = load(id);
		getSession().delete(entity);
	}

	public void delete(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		for (PK id : ids) {
			T entity = load(id);
			getSession().delete(entity);
		}
	}

	public void flush() {
		getSession().flush();
	}

	public void clear() {
		getSession().clear();
	}

	public void evict(Object object) {
		Assert.notNull(object, "object is required");
		getSession().evict(object);
	}
	
	public Pager findByPager(Pager pager) {
		if (pager == null) {
			pager = new Pager();
		}
			DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
			return findByPager(pager, detachedCriteria);
	}

	public Pager findByPager(Pager pager, DetachedCriteria detachedCriteria) {
		if (pager == null) {
			pager = new Pager();
		}
		
		Integer pageNumber = pager.getPageNumber();
		Integer pageSize = pager.getPageSize();
		String property = pager.getProperty();
		String keyword = pager.getKeyword();
		String orderBy = pager.getOrderBy();
		OrderType orderType = pager.getOrderType();
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		if (StringUtils.isNotEmpty(property) && StringUtils.isNotEmpty(keyword)) {
			String propertyString = "";
			if (property.contains(".")) {
				String propertyPrefix = StringUtils.substringBefore(property, ".");
				String propertySuffix = StringUtils.substringAfter(property, ".");
				criteria.createAlias(propertyPrefix, "model");
				propertyString = "model." + propertySuffix;
			} else {
				propertyString = property;
			}
			if(keyword.trim().contains("%")){
				criteria.add(Restrictions.sqlRestriction("{alias}."+propertyString+" like ? escape'/'", 
						"%"+StringUtils.replace(keyword.trim(), "%", "/%")+"%", Hibernate.STRING));
			}else{
				criteria.add(Restrictions.like(propertyString, "%" + keyword.trim() + "%"));
			}
		}
		Integer totalCount = Integer.parseInt(criteria.setProjection(Projections.rowCount()).uniqueResult().toString()) ;
		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		criteria.setFirstResult((pageNumber - 1) * pageSize);
		criteria.setMaxResults(pageSize);
		if (StringUtils.isNotEmpty(orderBy) && orderType != null) {
			if (orderType == OrderType.asc) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}
		pager.setTotalCount(totalCount);
		pager.setList(criteria.list());
		return pager;
	}

	public int excuteUpdate(String sql) {
		return jdbcTemplate.update(sql);
		/*getSession().doWork(   
				  new Work() {    
				    public void execute(Connection connection) {    
				      // 已获取connection，可以继续你的JDBC代码。    
				    	PreparedStatement pStmt;
						try {
							pStmt = connection.prepareStatement(sql);
							pStmt.executeUpdate();
						} catch (SQLException e) {
							try {
								connection.rollback();
							} catch (SQLException e1) {
								e1.printStackTrace();
							}
							e.printStackTrace();
						}
						 // 不能把这个connection关闭  
				    }   
				  }   
				);  */
		
	}
	
	public int[] excuteBatch( List<String> sqlList) {
		String[] sqlArr = sqlList.toArray(new String[0]); 
		return jdbcTemplate.batchUpdate(sqlArr);
		/*getSession().doWork(   
				  new Work() {    
				    public void execute(Connection connection) {    
				      // 已获取connection，可以继续你的JDBC代码。    
				    	Statement stmt = null;
						try {
							boolean autoCommit = connection.getAutoCommit();
							connection.setAutoCommit(false);
							stmt = connection.createStatement();
							int size = sqlList.size();
							if(size==0)return;
							for(int i=0;i<size;i++){
								String sql = sqlList.get(i);
								stmt.addBatch(sql);
								if(i!=0&&i%1000==0){
									stmt.executeBatch();
									connection.commit();
									stmt.clearBatch();
								}
							}
							stmt.executeBatch();
							connection.commit();
							connection.setAutoCommit(autoCommit);
						} catch (SQLException e) {
							try {
								connection.rollback();
							} catch (SQLException e1) {
								e1.printStackTrace();
							}
							e.printStackTrace();
						}
						 // 不能把这个connection关闭  
				    }   
				  }   
				);  */
		
	}
	
	public Connection getConnection(){
		
		/*ConnectionProvider cp =((SessionFactoryImplementor)sessionFactory).getConnectionProvider();  
		Connection conn = null;
		try {
			conn = cp.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}*/
		Connection conn  = getSession().connection();
		return conn;
	}
	
	public SqlRowSet executeQuery(String sql){
		SqlRowSet rs =jdbcTemplate.queryForRowSet(sql);
		return rs;
	}


	public boolean isExist(String[] propertyNames, Object[] values) {
		T object = get(propertyNames, values);	
		return (object != null);
	}
	
}